package com.fanrenqiji.wechat.config;


import me.chanjar.weixin.common.bean.WxAccessToken;
import me.chanjar.weixin.common.util.ToStringUtils;
import me.chanjar.weixin.common.util.http.ApacheHttpClientBuilder;
import me.chanjar.weixin.mp.api.WxMpConfigStorage;
import org.apache.http.ssl.SSLContexts;
import org.springframework.data.redis.core.RedisTemplate;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by yanwt on 2017/2/23.
 */
public class WxMpInRedisConfigStorage implements WxMpConfigStorage {

    /* Redis keys here */
    private static final String ACCESS_TOKEN_KEY = "WX_MP_ACCESS_TOKEN";
    private static final String ACCESS_TOKEN_EXPIRES_TIME_KEY = "WX_MP_ACCESS_TOKEN_EXPIRES_TIME";
    private static final String JS_API_TICKET_KEY = "WX_MP_JS_API_TICKET";
    private static final String JS_API_TICKET_EXPIRES_TIME_KEY = "WX_MP_JS_API_TICKET_EXPIRES_TIME";
    private static final String CARD_API_TICKET_KEY = "WX_MP_CARD_API_TICKET";
    private static final String CARD_API_TICKET_EXPIRES_TIME_KEY = "WX_MP_CARD_API_TICKET_EXPIRES_TIME";

    private RedisTemplate<String, String> redisTemplate;

    protected volatile String appId;
    protected volatile String secret;
    protected volatile String partnerId;
    protected volatile String partnerKey;
    protected volatile String notifyURL;
    protected volatile String tradeType;
    protected volatile String token;
    protected volatile String aesKey;
    protected volatile String oauth2redirectUri;
    protected volatile String httpProxyHost;
    protected volatile int httpProxyPort;
    protected volatile String httpProxyUsername;
    protected volatile String httpProxyPassword;
    protected Lock accessTokenLock = new ReentrantLock();
    protected Lock jsapiTicketLock = new ReentrantLock();
    protected Lock cardApiTicketLock = new ReentrantLock();
    protected volatile File tmpDirFile;
    protected volatile SSLContext sslContext;
    protected volatile ApacheHttpClientBuilder apacheHttpClientBuilder;

    public WxMpInRedisConfigStorage() {
    }

    public RedisTemplate<String, String> getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public String getAccessToken() {
        return redisTemplate.opsForValue().get(ACCESS_TOKEN_KEY);
    }

    public void setAccessToken(String accessToken) {
        redisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, accessToken);
    }

    public Lock getAccessTokenLock() {
        return this.accessTokenLock;
    }

    public boolean isAccessTokenExpired() {
        if (redisTemplate != null) {
            String expiresTimeStr = redisTemplate.opsForValue().get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
            if (expiresTimeStr != null) {
                Long expiresTime = Long.parseLong(expiresTimeStr);
                return System.currentTimeMillis() > expiresTime;
            }

            return true;
        }
        return true;
    }

    public synchronized void updateAccessToken(WxAccessToken accessToken) {
        this.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
    }

    public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
        redisTemplate.opsForValue().set(ACCESS_TOKEN_KEY, accessToken);
        redisTemplate.opsForValue().set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "" + (System.currentTimeMillis() + (long) (expiresInSeconds - 200) * 1000L));
    }

    public void expireAccessToken() {
        redisTemplate.opsForValue().set(ACCESS_TOKEN_EXPIRES_TIME_KEY, "0");
    }

    public String getJsapiTicket() {
        return redisTemplate.opsForValue().get(JS_API_TICKET_KEY);
    }

    public void setJsapiTicket(String jsapiTicket) {
        redisTemplate.opsForValue().set(JS_API_TICKET_KEY, jsapiTicket);
    }

    public Lock getJsapiTicketLock() {
        return this.jsapiTicketLock;
    }

    public boolean isJsapiTicketExpired() {
        if (redisTemplate != null) {
            String jsapiTicketExpiresTime = redisTemplate.opsForValue().get(JS_API_TICKET_EXPIRES_TIME_KEY);
            if (jsapiTicketExpiresTime != null) {
                Long expiresTime = Long.parseLong(jsapiTicketExpiresTime);
                return System.currentTimeMillis() > expiresTime;
            }
            return true;
        }
        return false;
    }

    public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {

        redisTemplate.opsForValue().set(JS_API_TICKET_KEY, jsapiTicket);
        redisTemplate.opsForValue().set(JS_API_TICKET_EXPIRES_TIME_KEY, "" + (System.currentTimeMillis() + (long) (expiresInSeconds - 200) * 1000L));
    }

    public void expireJsapiTicket() {
        redisTemplate.opsForValue().set(JS_API_TICKET_EXPIRES_TIME_KEY, "0");
    }

    public String getCardApiTicket() {
        return redisTemplate.opsForValue().get(CARD_API_TICKET_KEY);
    }

    public Lock getCardApiTicketLock() {
        return this.cardApiTicketLock;
    }

    public boolean isCardApiTicketExpired() {

        if (redisTemplate != null) {
            String cardapiTicketExpiresTime = redisTemplate.opsForValue().get(CARD_API_TICKET_EXPIRES_TIME_KEY);
            if (cardapiTicketExpiresTime != null) {
                Long expiresTime = Long.parseLong(cardapiTicketExpiresTime);
                return System.currentTimeMillis() > expiresTime;
            }
            return true;
        }
        return false;

    }

    public synchronized void updateCardApiTicket(String cardApiTicket, int expiresInSeconds) {
        redisTemplate.opsForValue().set(CARD_API_TICKET_KEY, cardApiTicket);
        redisTemplate.opsForValue().set(CARD_API_TICKET_EXPIRES_TIME_KEY, "" + (System.currentTimeMillis() + (long) (expiresInSeconds - 200) * 1000L));

    }

    public void expireCardApiTicket() {
        redisTemplate.opsForValue().set(CARD_API_TICKET_EXPIRES_TIME_KEY, "0");
    }

    public String getAppId() {
        return this.appId;
    }

    public void setAppId(String appId) {
        this.appId = appId;
    }

    public String getSecret() {
        return this.secret;
    }

    public void setSecret(String secret) {
        this.secret = secret;
    }

    public String getToken() {
        return this.token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public long getExpiresTime() {
        String expiresTimeStr = redisTemplate.opsForValue().get(ACCESS_TOKEN_EXPIRES_TIME_KEY);
        if (expiresTimeStr != null) {
            return  Long.parseLong(expiresTimeStr);
        }
        return 0L;
    }

    public String getAesKey() {
        return this.aesKey;
    }

    public void setAesKey(String aesKey) {
        this.aesKey = aesKey;
    }

    public String getOauth2redirectUri() {
        return this.oauth2redirectUri;
    }

    public void setOauth2redirectUri(String oauth2redirectUri) {
        this.oauth2redirectUri = oauth2redirectUri;
    }

    public String getHttpProxyHost() {
        return this.httpProxyHost;
    }

    public void setHttpProxyHost(String httpProxyHost) {
        this.httpProxyHost = httpProxyHost;
    }

    public int getHttpProxyPort() {
        return this.httpProxyPort;
    }

    public void setHttpProxyPort(int httpProxyPort) {
        this.httpProxyPort = httpProxyPort;
    }

    public String getHttpProxyUsername() {
        return this.httpProxyUsername;
    }

    public void setHttpProxyUsername(String httpProxyUsername) {
        this.httpProxyUsername = httpProxyUsername;
    }

    public String getHttpProxyPassword() {
        return this.httpProxyPassword;
    }

    public void setHttpProxyPassword(String httpProxyPassword) {
        this.httpProxyPassword = httpProxyPassword;
    }

    public String toString() {
        return ToStringUtils.toSimpleString(this);
    }

    public String getPartnerId() {
        return this.partnerId;
    }

    public void setPartnerId(String partnerId) {
        this.partnerId = partnerId;
    }

    public String getPartnerKey() {
        return this.partnerKey;
    }

    public void setPartnerKey(String partnerKey) {
        this.partnerKey = partnerKey;
    }

    public String getNotifyURL() {
        return this.notifyURL;
    }

    public void setNotifyURL(String notifyURL) {
        this.notifyURL = notifyURL;
    }

    public String getTradeType() {
        return this.tradeType;
    }

    public void setTradeType(String tradeType) {
        this.tradeType = tradeType;
    }

    public File getTmpDirFile() {
        return this.tmpDirFile;
    }

    public void setTmpDirFile(File tmpDirFile) {
        this.tmpDirFile = tmpDirFile;
    }

    public SSLContext getSslContext() {
        return this.sslContext;
    }

    public void setSslContext(SSLContext context) {
        this.sslContext = context;
    }

    public void setSslContextFilePath(String filePath) {
        if (null == this.partnerId) {
            throw new IllegalArgumentException("请设置partnerId的值");
        } else {
            File file = new File(filePath);
            if (!file.exists()) {
                throw new RuntimeException("证书文件：【" + file.getPath() + "】不存在！");
            } else {
                try {
                    FileInputStream inputStream = new FileInputStream(file);
                    KeyStore keystore = KeyStore.getInstance("PKCS12");
                    char[] partnerId2charArray = this.partnerId.toCharArray();
                    keystore.load(inputStream, partnerId2charArray);
                    this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();
                } catch (Exception var6) {
                    throw new RuntimeException("证书文件有问题，请核实！", var6);
                }
            }
        }
    }

    public ApacheHttpClientBuilder getApacheHttpClientBuilder() {
        return this.apacheHttpClientBuilder;
    }

    public void setApacheHttpClientBuilder(ApacheHttpClientBuilder apacheHttpClientBuilder) {
        this.apacheHttpClientBuilder = apacheHttpClientBuilder;
    }

    public boolean autoRefreshToken() {
        return true;
    }

    public boolean useSandboxForWxPay() {
        return false;
    }
}
